package org.dreamwork.plugins.network.assistant.gui

import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.components.JBScrollPane
import com.intellij.uiDesigner.core.GridConstraints
import com.intellij.uiDesigner.core.GridLayoutManager
import com.intellij.uiDesigner.core.Spacer
import org.dreamwork.plugins.network.assistant.gui.NetworkReader.NetworkReaderListener
import org.dreamwork.plugins.network.assistant.gui.SetupForm.OnSelectListener
import org.dreamwork.plugins.network.assistant.gui.TcpClientWorker.TcpClientListener
import org.dreamwork.plugins.network.assistant.gui.Utils.toHex
import org.dreamwork.ui.concurrent.Handler
import org.dreamwork.ui.concurrent.IMessageHandler
import org.dreamwork.ui.concurrent.Message
import org.dreamwork.util.IOUtil
import org.dreamwork.util.StringUtil
import org.dreamwork.util.Tools
import java.awt.*
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.io.*
import java.net.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.locks.ReentrantLock
import javax.swing.*

/**
 * Created by seth.yang on 2018/5/7
 */
class MainFrame : ActionListener, IMessageHandler, OnSelectListener, NetworkReaderListener, TcpClientListener {
    lateinit var content: JPanel private set

    private lateinit var rbTCP: JRadioButton
    private lateinit var rbUDP: JRadioButton
    private lateinit var rbServer: JRadioButton
    private lateinit var rbClient: JRadioButton
    private lateinit var txtRemote: JTextField
    private lateinit var btnOpen: JButton
    private lateinit var lblTarget: JLabel
    private lateinit var cbRedirect: JCheckBox
    private lateinit var cbReturn: JCheckBox
    private lateinit var cbShowTime: JCheckBox
    private lateinit var cbShowHex: JCheckBox
    private lateinit var cbPauseReceive: JCheckBox
    private lateinit var btnClear: JButton
    private lateinit var btnSave: JButton
    private lateinit var cbSendFile: JButton
    private lateinit var btnAppend: JCheckBox
    private lateinit var cbSendHex: JCheckBox
    private lateinit var cbAutoSend: JCheckBox
    private lateinit var txtInterval: JTextField
    private lateinit var txtReceive: JTextPane
    private lateinit var btnSend: JButton
    private lateinit var txtSend: JTextArea
    private lateinit var cbClients: JComboBox<String?>
    private lateinit var btnClose: JButton
    private lateinit var pClientList: JPanel
    private lateinit var cbEcho: JCheckBox
    private lateinit var lblLink: JLabel
    private lateinit var icon: CircleIcon
    private lateinit var chooser: JFileChooser

    private lateinit var logger: IMessageLogger
    private lateinit var worker: MessageSendWorker
    private lateinit var server: ServerSocket

    private var adapter: INetworkAdapter? = null
    private var reader: NetworkReader? = null
    private var future: Future<*>? = null
    private var config: Config? = null

    private var protocol = TCP
    private var role = CLIENT
    private var isConnected = false
    private val clients: MutableMap<String, TcpClientWorker?> = ConcurrentHashMap()
    private val executor: ExecutorService = Executors.newCachedThreadPool()
    private val handler: Handler = Handler(this)

    private fun guiSetup() {
//        setSize (WIDTH, HEIGHT);
//        setMinimumSize (new Dimension (WIDTH, HEIGHT));
//        val D = Toolkit.getDefaultToolkit().screenSize
        //        int x = (D.width - WIDTH) / 2, y = (D.height - HEIGHT) / 2;
//        setLocation (x, y);
        icon = CircleIcon()
        btnOpen.icon = icon
        rbTCP.isSelected = true
        rbClient.isSelected = true

//        URL url = getClass ().getClassLoader ().getResource ("network.png");
//        ImageIcon icon = new ImageIcon (url, null);
//        setIconImage (icon.getImage ());
        var group = ButtonGroup()
        group.add(rbTCP)
        group.add(rbUDP)

        group = ButtonGroup()
        group.add(rbServer)
        group.add(rbClient)
        //        setContentPane (root);
        cbShowTime.isSelected = true
        cbShowHex.isSelected = true
        cbReturn.isSelected = true
        btnSend.isEnabled = false
        chooser = JFileChooser()
        logger = TextAreaLogger(txtReceive, showTime = true, showAscii = false)
        txtReceive.isEditable = false
        cbClients.addItem("所有客户端")

        // network settings
        rbClient.addActionListener(this)
        rbServer.addActionListener(this)
        rbTCP.addActionListener(this)
        rbUDP.addActionListener(this)
        btnOpen.addActionListener(this)

        // receiver settings
        cbRedirect.addActionListener(this)
        cbReturn.addActionListener(this)
        cbShowTime.addActionListener(this)
        cbShowHex.addActionListener(this)
        cbPauseReceive.addActionListener(this)
        btnClear.addActionListener(this)
        btnSave.addActionListener(this)

        // sender settings
        cbSendHex.addActionListener(this)
        btnAppend.addActionListener(this)
        cbAutoSend.addActionListener(this)
        cbSendFile.addActionListener(this)

        // send panel
        btnClose.addActionListener(this)
        btnSend.addActionListener(this)
        pClientList.isVisible = false
        val color = lblLink.foreground
        lblLink.addMouseListener(object : MouseAdapter() {
            override fun mouseClicked(e: MouseEvent) {
                try {
                    val url = URL(GIT_ADDRESS)
                    val uri = url.toURI()
                    Desktop.getDesktop().browse(uri)
                } catch (ex: Exception) {
                    ex.printStackTrace()
                }
            }

            override fun mouseEntered(e: MouseEvent) {
                lblLink.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
                lblLink.text = "<html><u>$GIT_ADDRESS</u></html>"
                lblLink.foreground = Color.RED
            }

            override fun mouseExited(e: MouseEvent) {
                lblLink.cursor = Cursor.getDefaultCursor()
                lblLink.text = GIT_ADDRESS
                lblLink.foreground = color
            }
        })
    }

    private fun init() {
        worker = MessageSendWorker()
        worker.pause = true
        executor.execute(worker)
        executor.execute(logger as Runnable)
    }

    override fun actionPerformed(e: ActionEvent) {
        val sender = e.source
        if (sender === rbClient) {
            role = CLIENT
            lblTarget.text = "远程地址和端口"
            cbEcho.isEnabled = false
            cbSendFile.isEnabled = true
        } else if (sender === rbServer) {
            role = SERVER
            lblTarget.text = "本地端口"
            cbEcho.isEnabled = true
            cbSendFile.isEnabled = false
        } else if (sender === rbTCP) {
            protocol = TCP
        } else if (sender === rbUDP) {
            protocol = UDP
        } else if (sender === cbShowHex) {
            logger.setShowAscii(!cbShowHex.isSelected)
        } else if (sender === cbShowTime) {
            logger.setShowTime(cbShowTime.isSelected)
        } else if (sender === cbReturn) {
            logger.setAutoReturn(cbReturn.isSelected)
        } else if (sender === cbRedirect) {
            if (cbRedirect.isSelected) {
                redirectToFile()
            } else {
                logger.dumpTo(null)
            }
        } else if (sender === cbSendFile) {
            sendFile()
        } else if (sender === btnClear) {
            txtReceive.text = null
        } else if (sender === btnSave) {
            saveData()
        } else if (sender === btnAppend) {
            if (btnAppend.isSelected) {
                val form = SetupForm("设置")
                form.setOnSelectListener(this)
                form.show ()
            } else {
                config = null
            }
        } else if (sender === cbAutoSend) {
            if (cbAutoSend.isSelected) {
                btnSend.text = "开始发送"
            } else {
                worker.pause = true
                btnSend.text = "停止发送"
            }
        } else if (sender === btnOpen) {
            if (isConnected) {
                disconnect()
            } else {
                handler.sendNonUIMessage(CONNECT)
            }
        } else if (sender === btnSend) {
            if (isConnected) {
                handler.sendNonUIMessage(SEND_DATA)
            }
        } else if (sender === btnClose) {
            closeClient()
        }
    }

    private fun closeClient() {
        val name = cbClients.selectedItem as String
        if (!clients.containsKey(name)) {
            showError("请选择要关闭的客户端")
            return
        }
        val worker = clients[name]
        try {
            worker!!.close()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            clients.remove(name)
        }
    }

    private fun closeAllClients() {
        for (name in clients.keys) {
            val worker = clients[name]
            try {
                worker!!.close()
            } catch (ex: IOException) {
                ex.printStackTrace()
            }
        }
        clients.clear()
        cbClients.removeAllItems()
        cbClients.addItem("所有客户端")
    }

    private fun redirectToFile() {
        chooser.showSaveDialog(null)
        val file = chooser.selectedFile ?: return
        if (!checkParent(file)) {
            return
        }
        try {
            logger.dumpTo(FileOutputStream(file))
        } catch (ex: FileNotFoundException) {
            showError(ex.message)
            logger.append(ex.message!!.toByteArray(), IMessageLogger.ERROR)
        }
    }

    private fun checkParent(file: File): Boolean {
        val parent = file.parentFile
        if (!parent.exists() && !parent.mkdirs()) {
            try {
                showError("不能创建文件" + file.canonicalPath)
            } catch (e: IOException) {
                e.printStackTrace()
            }
            return false
        }
        return true
    }

    private fun saveData() {
        if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
            val file = chooser.selectedFile
            if (file != null) {
                if (!checkParent(file)) {
                    return
                }
                handler.sendNonUIMessage(SAVE_DATA, file)
            }
        }
    }

    private fun saveData(file: File) {
        try {
            FileOutputStream(file).use { out ->
                val text = txtReceive.text
                out.write(text.toByteArray())
                out.flush()
                showMessage("保存成功")
            }
        } catch (ex: IOException) {
            showError("保存失败")
        }
    }

    private fun sendFile() {
        if (!isConnected) {  // not connect
            return
        }
        if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
            val file = chooser.selectedFile
            if (file != null) {
                handler.sendNonUIMessage(SEND_FILE, file)
            }
        }
    }

    private fun sendFile(file: File) {
        val size = file.length()
        if (size > 1 shl 20) { // the file length more than 1M
            showError("不能发送超过1M的文件")
            return
        }
        try {
            var buff = IOUtil.read(file)
            if (adapter != null) {
                if (cbSendHex.isSelected) {
                    val text = String(buff!!)
                    buff = toHex(text)
                }
                adapter!!.write(buff!!)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun connect() {
        try {
            if (protocol == TCP) {
                if (role == CLIENT) {
                    try {
                        if (connectToTcp()) {
                            setUIState(true)
                        }
                    } catch (ex: Exception) {
                        showError(ex.message)
                    }
                } else {
                    try {
                        createTcpServer()
                        isConnected = true
                        icon.state = 1
                        btnOpen.text = "断开连接"
                        btnSend.isEnabled = true
                        pClientList.isVisible = true
                    } catch (ex: NumberFormatException) {
                        showError("无效的端口号，请输入 1~65534的整数")
                    } catch (ex: Exception) {
                        showError(ex.message)
                    }
                }
            } else {
                if (role == CLIENT) {
                    connectToUdp()
                }
            }
        } catch (ex: IOException) {
            ex.printStackTrace()
        }
    }

    @Throws(Exception::class)
    private fun createTcpServer() {
        val s_port = txtRemote.text
        val port = s_port.toInt()
        if (port <= 0 || port >= 65535) {
            throw NumberFormatException()
        }
        server = ServerSocket(port)
        logger.append(("TCP Server 监听在" + server.localSocketAddress).toByteArray(), IMessageLogger.INFO)
        future = executor.submit {
            val thread = Thread.currentThread()
            thread.name = "TCPServer"
            while (!thread.isInterrupted) try {
                val socket = server.accept()
                TcpClientWorker(socket,  /*logger, */executor, this@MainFrame)
                logger.append(("收到客户端" + socket.remoteSocketAddress + "连接").toByteArray(), IMessageLogger.INFO)
            } catch (ex: Exception) {
                logger.append(ex.message!!.toByteArray(), IMessageLogger.ERROR)
            }
        }
    }

    private fun disconnect() {
        isConnected = false
        icon.state = 0
        btnOpen.text = "打开连接"
        if (adapter != null) {
            val tmp: INetworkAdapter = adapter!!
            adapter = null
            try {
                tmp.close()
            } catch (ex: IOException) {
                ex.printStackTrace()
            }
        }
        worker.pause = true
        btnSend.isEnabled = false

        if (reader != null) {
            reader!!.shutdown()
        }
        reader = null
        if (protocol == TCP && role == SERVER) {
            pClientList.isVisible = false
        }
        if (future != null) future?.cancel(true)
        closeAllClients()
        try {
            server.close()
        } catch (ex: IOException) {
            ex.printStackTrace()
        }
        setUIState(false)
    }

    @Throws(IOException::class)
    private fun connectToTcp(): Boolean {
        val host = hostInfo
        return if (host != null) {
            val socket = Socket(host.host, host.port)
            adapter = TcpAdapter(socket)
            reader = TcpReader(socket.getInputStream() /*, logger*/)
            reader!!.listener = this
            executor.execute(reader!!)
            icon.state = 1
            isConnected = true
            btnOpen.text = "关闭连接"
            worker.adapter = adapter
            btnSend.isEnabled = true
            true
        } else {
            false
        }
    }

    @Throws(IOException::class)
    private fun connectToUdp() {
        val host = hostInfo
        if (host != null) {
            val buff = ByteArray(10240)
            val address = InetAddress.getByName(host.host)
            val socket = DatagramSocket()
            val packet = DatagramPacket(buff, buff.size, address, host.port)
            adapter = UdpAdapter(socket, packet)
            reader = UdpReader(socket, packet /*, logger*/)
            reader!!.listener = this
            executor.execute(reader!!)
            icon.state = 1
            isConnected = true
            btnOpen.text = "关闭连接"
            worker.adapter = adapter
            btnSend.isEnabled = true
        }
    }

    private fun showError(message: String?) {
        handler.sendUIMessage(SHOW_ERROR, message)
    }

    private fun showMessage(message: String) {
        handler.sendUIMessage(SHOW_MSG, message)
    }

    override fun handleUIMessage(message: Message) {
        when (message.what) {
            SHOW_ERROR -> JOptionPane.showMessageDialog(null, message.obj, "错误", JOptionPane.ERROR_MESSAGE)
            SHOW_MSG -> JOptionPane.showMessageDialog(null, message.obj, "信息", JOptionPane.INFORMATION_MESSAGE)
        }
    }

    override fun handleNonUIMessage(message: Message) {
        try {
            when (message.what) {
                CONNECT -> connect()
                SEND_DATA -> sendData()
                SEND_FILE -> sendFile(message.obj as File)
                SAVE_DATA -> saveData(message.obj as File)
            }
        } catch (ex: Exception) {
            ex.printStackTrace()
            showError(ex.message)
            logger.append(ex.message!!.toByteArray(), IMessageLogger.ERROR)
        }
    }

    private val hostInfo: Host?
        get() {
            val text = txtRemote.text
            val pos = text.lastIndexOf(":")
            if (pos < 0) {
                showError("请设置远程地址和端口")
                return null
            }
            val host = text.substring(0, pos)
            val s_port = text.substring(pos + 1)
            val port: Int
            port = try {
                s_port.toInt()
            } catch (ex: Exception) {
                showError("请设置远程地址和端口")
                ex.printStackTrace()
                return null
            }
            val h = Host()
            h.host = host
            h.port = port
            return h
        }

    @Throws(IOException::class)
    private fun sendData() {
        if (!worker.pause) {
            worker.pause = true
            btnSend.text = "开始发送"
        } else {
            val text = txtSend.text
            if (StringUtil.isEmpty(text)) {
                return
            }
            var buff: ByteArray
            if (cbSendHex.isSelected) {
                buff = toHex(text)
                if (config != null) {
                    buff = appendData(buff)
                }
            } else {
                buff = text.toByteArray()
            }
            if (adapter != null) {
                if (cbAutoSend.isSelected) {
                    worker.adapter = adapter
                    worker.data = buff
                    try {
                        worker.interval = txtInterval.text.toInt().toLong()
                    } catch (ex: Exception) {
                        // ignore
                    }
                    worker.resume()
                    btnSend.text = "停止发送"
                } else {
                    adapter?.write(buff)
                }
            } else if (role == SERVER && protocol == TCP) {
                val name = cbClients.selectedItem as String
                if (clients.containsKey(name)) {
                    val worker = clients[name]
                    worker!!.write(buff)
                } else for (worker in clients.values) {
                    try {
                        worker!!.write(buff)
                    } catch (ex: IOException) {
                        logger.append(("Can't write data to " + worker!!.name).toByteArray(), IMessageLogger.ERROR)
                    }
                }
            }
        }
    }

    @Throws(IOException::class)
    private fun appendData(data: ByteArray): ByteArray {
        if (config == null) return data
        if (config!!.head == null && config!!.tail == null && config!!.type == null) {
            return data
        }
        val baos = ByteArrayOutputStream()
        if (config!!.head != null && config!!.head.isNotEmpty()) {
            baos.write(config!!.head)
        }
        baos.write(data)
        if (config!!.type != null) when (config?.type) {
            CRCType.NSum8, CRCType.Sum8 -> {
                val sum: Int = if (config!!.includeHead && config!!.head != null) {
                    Tools.checksum8(baos.toByteArray())
                } else {
                    Tools.checksum8(data)
                }
                baos.write(if (config!!.type === CRCType.Sum8) sum else -sum)
            }
            CRCType.XOR8 -> {
                val sum: Int = if (config!!.includeHead && config!!.head != null) {
                    Tools.xor(baos.toByteArray())
                } else {
                    Tools.xor(data)
                }
                baos.write(sum)
            }
            CRCType.CRC16 -> {
                val sum: ByteArray = if (config!!.includeHead && config!!.head != null) {
                    Tools.crc16(baos.toByteArray())
                } else {
                    Tools.crc16(data)
                }
                baos.write(sum)
            }
        }
        if (config!!.tail != null && config!!.tail.isNotEmpty()) {
            baos.write(config!!.tail)
        }
        return baos.toByteArray()
    }

    override fun accept(config: Config?) {
        this.config = config
        if (config == null) {
            btnAppend.isSelected = false
        }
    }

    override fun onClosed() {
        disconnect()
        logger.append("对端断开连接".toByteArray(), IMessageLogger.INFO)
    }

    override fun onReceived(data: ByteArray?) {
        logger.append(data)
        if (cbEcho.isSelected) {
            if (adapter != null) {
                try {
                    adapter!!.write(data!!)
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            } else if (clients.isNotEmpty()) {
                for (worker in clients.values) {
                    try {
                        worker!!.write(data!!)
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
            }
        }
    }

    private fun setUIState(locked: Boolean) {
        rbUDP.isEnabled = !locked
        rbTCP.isEnabled = !locked
        rbServer.isEnabled = !locked
        rbClient.isEnabled = !locked
        txtRemote.isEnabled = !locked
    }

    override fun onConnected(worker: TcpClientWorker?) {
        cbClients.addItem(worker!!.name)
        clients[worker.name] = worker
    }

    override fun onDisconnected(worker: TcpClientWorker?) {
        cbClients.removeItem(worker!!.name)
        clients.remove(worker.name)
        logger.append((worker.name + "断开连接").toByteArray(), IMessageLogger.INFO)
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private fun initialComponents () {
        content = JPanel()
        content.layout = GridLayoutManager(7, 3, Insets(0, 0, 0, 0), -1, -1)
        content.border = BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5), null)
        val panel1 = JPanel()
        panel1.layout = GridLayoutManager(7, 2, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel1, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        panel1.border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "网络设置")
        val label1 = JLabel()
        label1.text = "协议"
        panel1.add(label1, GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        rbTCP = JRadioButton()
        rbTCP.text = "TCP"
        panel1.add(rbTCP, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        rbUDP = JRadioButton()
        rbUDP.text = "UDP"
        panel1.add(rbUDP, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val label2 = JLabel()
        label2.text = "角色"
        panel1.add(label2, GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        rbServer = JRadioButton()
        rbServer.text = "服务端"
        panel1.add(rbServer, GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        rbClient = JRadioButton()
        rbClient.text = "客户端"
        panel1.add(rbClient, GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        lblTarget = JLabel()
        lblTarget.text = "远程地址"
        panel1.add(lblTarget, GridConstraints(4, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        txtRemote = JTextField()
        panel1.add(txtRemote, GridConstraints(5, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, Dimension(150, -1), null, 0, false))
        btnOpen = JButton()
        btnOpen.text = "打开"
        panel1.add(btnOpen, GridConstraints(6, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val panel2 = JPanel()
        panel2.layout = GridLayoutManager(7, 2, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel2, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        panel2.border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "接收区设置")
        cbRedirect = JCheckBox()
        cbRedirect.text = "接受转向文件..."
        panel2.add(cbRedirect, GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbReturn = JCheckBox()
        cbReturn.text = "自动换行"
        panel2.add(cbReturn, GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbShowTime = JCheckBox()
        cbShowTime.text = "显示接收时间"
        panel2.add(cbShowTime, GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbShowHex = JCheckBox()
        cbShowHex.text = "十六进制显示"
        panel2.add(cbShowHex, GridConstraints(3, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbPauseReceive = JCheckBox()
        cbPauseReceive.text = "暂停接收显示"
        panel2.add(cbPauseReceive, GridConstraints(4, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        btnClear = JButton()
        btnClear.text = "清除接收"
        panel2.add(btnClear, GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        btnSave = JButton()
        btnSave.text = "保存..."
        panel2.add(btnSave, GridConstraints(6, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbEcho = JCheckBox()
        cbEcho.isEnabled = false
        cbEcho.text = "自动回显"
        panel2.add(cbEcho, GridConstraints(5, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val panel3 = JPanel()
        panel3.layout = GridLayoutManager(5, 3, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel3, GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        panel3.border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "发送器设置")
        cbSendFile = JButton()
        cbSendFile.text = "发送文件内容..."
        panel3.add(cbSendFile, GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val spacer1 = Spacer()
        panel3.add(spacer1, GridConstraints(4, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        btnAppend = JCheckBox()
        btnAppend.text = "自动发送附加位"
        panel3.add(btnAppend, GridConstraints(1, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbSendHex = JCheckBox()
        cbSendHex.text = "按十六进制发送"
        panel3.add(cbSendHex, GridConstraints(2, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbAutoSend = JCheckBox()
        cbAutoSend.text = "自动发送"
        panel3.add(cbAutoSend, GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        txtInterval = JTextField()
        txtInterval.text = "1000"
        panel3.add(txtInterval, GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, Dimension(50, -1), null, 0, false))
        val label3 = JLabel()
        label3.text = "ms"
        panel3.add(label3, GridConstraints(3, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val panel4 = JPanel()
        panel4.layout = GridLayoutManager(1, 1, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel4, GridConstraints(0, 1, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        panel4.border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "接收区")
        val scrollPane1 = JBScrollPane ()
        panel4.add(scrollPane1, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        txtReceive = JTextPane()
        txtReceive.text = ""
        scrollPane1.setViewportView(txtReceive)
        val panel5 = JPanel()
        panel5.layout = GridLayoutManager(3, 2, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel5, GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        panel5.border = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "发送区")
        val scrollPane2 = JBScrollPane ()
        panel5.add(scrollPane2, GridConstraints(0, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        txtSend = JTextArea()
        scrollPane2.setViewportView(txtSend)
        btnSend = JButton()
        btnSend.text = "发送"
        panel5.add(btnSend, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val spacer2 = Spacer()
        panel5.add(spacer2, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        val spacer3 = Spacer()
        panel5.add(spacer3, GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false))
        val spacer4 = Spacer()
        content.add(spacer4, GridConstraints(0, 2, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        val spacer5 = Spacer()
        content.add(spacer5, GridConstraints(6, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false))
        pClientList = JPanel()
        pClientList.layout = GridLayoutManager(2, 3, Insets(0, 0, 0, 0), -1, -1)
        content.add(pClientList, GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        val label4 = JLabel()
        label4.text = "客户端"
        pClientList.add(label4, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        cbClients = ComboBox<String?> ()
        pClientList.add(cbClients, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        btnClose = JButton()
        btnClose.text = "断开"
        pClientList.add(btnClose, GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val spacer6 = Spacer()
        pClientList.add(spacer6, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false))
        val spacer7 = Spacer()
        content.add(spacer7, GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false))
        val panel6 = JPanel()
        panel6.layout = GridLayoutManager(2, 2, Insets(0, 0, 0, 0), -1, -1)
        content.add(panel6, GridConstraints(5, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false))
        lblLink = JLabel()
        lblLink.text = "https://gitee.com/seth_yang/dreamwork/tree/master/modules/assistant"
        panel6.add(lblLink, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val label5 = JLabel()
        label5.text = "源码下载"
        panel6.add(label5, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false))
        val spacer8 = Spacer()
        panel6.add(spacer8, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false))
    }

    internal class Host {
        var host: String? = null
        var port = 0
    }

    internal class MessageSendWorker : Runnable {
        var interval: Long = 1000
        private val locker = ReentrantLock ()
        private val c      = locker.newCondition ()
        private var running = true
        var pause = true
        var data: ByteArray? = null
        var adapter: INetworkAdapter? = null

        fun resume() {
            pause = false
            locker.lock()
            try {
                c.signalAll()
            } finally {
                locker.unlock()
            }
        }

        override fun run() {
            while (running) {
                locker.lock()
                try {
                    while (pause) {
                        c.await()
                    }
                } finally {
                    locker.unlock()
                }

                if (data != null && adapter != null) {
                    try {
                        adapter!!.write(data!!)
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
                try {
                    Thread.sleep(interval)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }
        }
    }

    companion object {
        private const val TCP = 0
        private const val UDP = 1
        private const val CLIENT = 0
        private const val SERVER = 1

        //    private static final int WIDTH = 960, HEIGHT = 650;
        private const val GIT_ADDRESS = "https://gitee.com/seth_yang/dreamwork-assistant-tools"
        private const val SHOW_MSG = -2
        private const val SHOW_ERROR = -1
        private const val CONNECT = 0
        private const val SEND_FILE = 1
        private const val SEND_DATA = 2
        private const val SAVE_DATA = 3
    }

    init {
//        super ("网络调试助手");

        initialComponents ()

        guiSetup()
        //        setVisible (true);
        init()

//        setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE);
    }
}